/****************************************************************************
 * apps/graphics/pdcurses/pdc_border.c
 * Public Domain Curses
 * RCSID("$Id: border.c,v 1.53 2008/07/13 16:08:18 wmcbrine Exp $")
 *
 *   Copyright (C) 2017 Gregory Nutt. All rights reserved.
 *   Adapted by: Gregory Nutt <gnutt@nuttx.org>
 *
 * Adapted from the original public domain pdcurses by Gregory Nutt and
 * released as part of NuttX under the 3-clause BSD license:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/* Name: border
 *
 * Synopsis:
 *       int border(chtype ls, chtype rs, chtype ts, chtype bs, chtype tl,
 *                  chtype tr, chtype bl, chtype br);
 *       int wborder(WINDOW *win, chtype ls, chtype rs, chtype ts,
 *                   chtype bs, chtype tl, chtype tr, chtype bl, chtype br);
 *       int box(WINDOW *win, chtype verch, chtype horch);
 *       int hline(chtype ch, int n);
 *       int vline(chtype ch, int n);
 *       int whline(WINDOW *win, chtype ch, int n);
 *       int wvline(WINDOW *win, chtype ch, int n);
 *       int mvhline(int y, int x, chtype ch, int n);
 *       int mvvline(int y, int x, chtype ch, int n);
 *       int mvwhline(WINDOW *win, int y, int x, chtype ch, int n);
 *       int mvwvline(WINDOW *win, int y, int x, chtype ch, int n);
 *
 *       int border_set(const cchar_t *ls, const cchar_t *rs,
 *                      const cchar_t *ts, const cchar_t *bs,
 *                      const cchar_t *tl, const cchar_t *tr,
 *                      const cchar_t *bl, const cchar_t *br);
 *       int wborder_set(WINDOW *win, const cchar_t *ls, const cchar_t *rs,
 *                       const cchar_t *ts, const cchar_t *bs,
 *                       const cchar_t *tl, const cchar_t *tr,
 *                       const cchar_t *bl, const cchar_t *br);
 *       int box_set(WINDOW *win, const cchar_t *verch, const cchar_t *horch);
 *       int hline_set(const cchar_t *wch, int n);
 *       int vline_set(const cchar_t *wch, int n);
 *       int whline_set(WINDOW *win, const cchar_t *wch, int n);
 *       int wvline_set(WINDOW *win, const cchar_t *wch, int n);
 *       int mvhline_set(int y, int x, const cchar_t *wch, int n);
 *       int mvvline_set(int y, int x, const cchar_t *wch, int n);
 *       int mvwhline_set(WINDOW *win, int y, int x, const cchar_t *wch, int n);
 *       int mvwvline_set(WINDOW *win, int y, int x, const cchar_t *wch, int n);
 *
 * Description:
 *       border(), wborder(), and box() draw a border around the edge of
 *       the window. If any argument is zero, an appropriate default is
 *       used:
 *
 *               ls      left side of border             ACS_VLINE
 *               rs      right side of border            ACS_VLINE
 *               ts      top side of border              ACS_HLINE
 *               bs      bottom side of border           ACS_HLINE
 *               tl      top left corner of border       ACS_ULCORNER
 *               tr      top right corner of border      ACS_URCORNER
 *               bl      bottom left corner of border    ACS_LLCORNER
 *               br      bottom right corner of border   ACS_LRCORNER
 *
 *       hline() and whline() draw a horizontal line, using ch, starting
 *       from the current cursor position. The cursor position does not
 *       change. The line is at most n characters long, or as many as
 *       will fit in the window.
 *
 *       vline() and wvline() draw a vertical line, using ch, starting
 *       from the current cursor position. The cursor position does not
 *       change. The line is at most n characters long, or as many as
 *       will fit in the window.
 *
 * Return Value:
 *       These functions return OK on success and ERR on error.
 *
 * Portability                                X/Open    BSD    SYS V
 *       border                                  Y       -      4.0
 *       wborder                                 Y       -      4.0
 *       box                                     Y       Y       Y
 *       hline                                   Y       -      4.0
 *       vline                                   Y       -      4.0
 *       whline                                  Y       -      4.0
 *       wvline                                  Y       -      4.0
 *       mvhline                                 Y
 *       mvvline                                 Y
 *       mvwhline                                Y
 *       mvwvline                                Y
 *       border_set                              Y
 *       wborder_set                             Y
 *       box_set                                 Y
 *       hline_set                               Y
 *       vline_set                               Y
 *       whline_set                              Y
 *       wvline_set                              Y
 *       mvhline_set                             Y
 *       mvvline_set                             Y
 *       mvwhline_set                            Y
 *       mvwvline_set                            Y
 */

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include "curspriv.h"

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/* _attr_passthru() -- Takes a single chtype 'ch' and checks if the
 *  current attribute of window 'win', as set by wattrset(), and/or the
 *  current background of win, as set by wbkgd(), should by combined with
 *  it. Attributes set explicitly in ch take precedence.
 */

static chtype _attr_passthru(WINDOW *win, chtype ch)
{
  chtype attr;

  /* If the incoming character doesn't have its own attribute, then use the
   * current attributes for the window. If the incoming character has
   * attributes, but not a color component, OR the attributes to the current
   * attributes for the window. If the incoming character has a color
   * component, use only the attributes from the incoming character.
   */

  attr = ch & A_ATTRIBUTES;
  if (!(attr & A_COLOR))
    {
      attr |= win->_attrs;
    }

  /* wrs (4/10/93) -- Apply the same sort of logic for the window background,
   * in that it only takes precedence if other color attributes are not there.
   */

  if (!(attr & A_COLOR))
    {
      attr |= win->_bkgd & A_ATTRIBUTES;
    }
  else
    {
      attr |= win->_bkgd & (A_ATTRIBUTES ^ A_COLOR);
    }

  ch = (ch & A_CHARTEXT) | attr;

  return ch;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

int wborder(WINDOW *win, chtype ls, chtype rs, chtype ts, chtype bs,
            chtype tl, chtype tr, chtype bl, chtype br)
{
  int xmax;
  int ymax;
  int i;

  PDC_LOG(("wborder() - called\n"));

  if (!win)
    {
      return ERR;
    }

  ymax = win->_maxy - 1;
  xmax = win->_maxx - 1;

  ls = _attr_passthru(win, ls ? ls : ACS_VLINE);
  rs = _attr_passthru(win, rs ? rs : ACS_VLINE);
  ts = _attr_passthru(win, ts ? ts : ACS_HLINE);
  bs = _attr_passthru(win, bs ? bs : ACS_HLINE);
  tl = _attr_passthru(win, tl ? tl : ACS_ULCORNER);
  tr = _attr_passthru(win, tr ? tr : ACS_URCORNER);
  bl = _attr_passthru(win, bl ? bl : ACS_LLCORNER);
  br = _attr_passthru(win, br ? br : ACS_LRCORNER);

  for (i = 1; i < xmax; i++)
    {
      win->_y[0][i]    = ts;
      win->_y[ymax][i] = bs;
    }

  for (i = 1; i < ymax; i++)
    {
      win->_y[i][0]    = ls;
      win->_y[i][xmax] = rs;
    }

  win->_y[0][0]        = tl;
  win->_y[0][xmax]     = tr;
  win->_y[ymax][0]     = bl;
  win->_y[ymax][xmax]  = br;

  for (i = 0; i <= ymax; i++)
    {
      win->_firstch[i] = 0;
      win->_lastch[i]  = xmax;
    }

  PDC_sync(win);

  return OK;
}

int border(chtype ls, chtype rs, chtype ts, chtype bs, chtype tl,
           chtype tr, chtype bl, chtype br)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("border() - called\n"));

  return wborder(stdscr, ls, rs, ts, bs, tl, tr, bl, br);
}

int box(WINDOW *win, chtype verch, chtype horch)
{
  PDC_LOG(("box() - called\n"));

  return wborder(win, verch, verch, horch, horch, 0, 0, 0, 0);
}

int whline(WINDOW *win, chtype ch, int n)
{
  chtype *dest;
  int startpos, endpos;

  PDC_LOG(("whline() - called\n"));

  if (!win || n < 1)
    {
      return ERR;
    }

  startpos = win->_curx;
  endpos = min(startpos + n, win->_maxx) - 1;
  dest = win->_y[win->_cury];
  ch = _attr_passthru(win, ch ? ch : ACS_HLINE);

  for (n = startpos; n <= endpos; n++)
    {
      dest[n] = ch;
    }

  n = win->_cury;

  if (startpos < win->_firstch[n] || win->_firstch[n] == _NO_CHANGE)
    {
      win->_firstch[n] = startpos;
    }

  if (endpos > win->_lastch[n])
    {
      win->_lastch[n] = endpos;
    }

  PDC_sync(win);

  return OK;
}

int hline(chtype ch, int n)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("hline() - called\n"));

  return whline(stdscr, ch, n);
}

int mvhline(int y, int x, chtype ch, int n)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("mvhline() - called\n"));

  if (move(y, x) == ERR)
    {
      return ERR;
    }

  return whline(stdscr, ch, n);
}

int mvwhline(WINDOW *win, int y, int x, chtype ch, int n)
{
  PDC_LOG(("mvwhline() - called\n"));

  if (wmove(win, y, x) == ERR)
    {
      return ERR;
    }

  return whline(win, ch, n);
}

int wvline(WINDOW *win, chtype ch, int n)
{
  int endpos;
  int x;

  PDC_LOG(("wvline() - called\n"));

  if (!win || n < 1)
    {
      return ERR;
    }

  endpos = min(win->_cury + n, win->_maxy);
  x = win->_curx;

  ch = _attr_passthru(win, ch ? ch : ACS_VLINE);

  for (n = win->_cury; n < endpos; n++)
    {
      win->_y[n][x] = ch;

      if (x < win->_firstch[n] || win->_firstch[n] == _NO_CHANGE)
        {
          win->_firstch[n] = x;
        }

      if (x > win->_lastch[n])
        {
          win->_lastch[n] = x;
        }
    }

  PDC_sync(win);

  return OK;
}

int vline(chtype ch, int n)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("vline() - called\n"));

  return wvline(stdscr, ch, n);
}

int mvvline(int y, int x, chtype ch, int n)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("mvvline() - called\n"));

  if (move(y, x) == ERR)
    {
      return ERR;
    }

  return wvline(stdscr, ch, n);
}

int mvwvline(WINDOW *win, int y, int x, chtype ch, int n)
{
  PDC_LOG(("mvwvline() - called\n"));

  if (wmove(win, y, x) == ERR)
    {
      return ERR;
    }

  return wvline(win, ch, n);
}

#ifdef CONFIG_PDCURSES_WIDE
int wborder_set(WINDOW *win, const cchar_t *ls, const cchar_t *rs,
                const cchar_t *ts, const cchar_t *bs, const cchar_t *tl,
                const cchar_t *tr, const cchar_t *bl, const cchar_t *br)
{
  PDC_LOG(("wborder_set() - called\n"));

  return wborder(win, ls ? *ls : 0, rs ? *rs : 0, ts ? *ts : 0,
                 bs ? *bs : 0, tl ? *tl : 0, tr ? *tr : 0,
                 bl ? *bl : 0, br ? *br : 0);
}

int border_set(const cchar_t *ls, const cchar_t *rs, const cchar_t *ts,
               const cchar_t *bs, const cchar_t *tl, const cchar_t *tr,
               const cchar_t *bl, const cchar_t *br)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("border_set() - called\n"));

  return wborder_set(stdscr, ls, rs, ts, bs, tl, tr, bl, br);
}

int box_set(WINDOW *win, const cchar_t *verch, const cchar_t *horch)
{
  PDC_LOG(("box_set() - called\n"));

  return wborder_set(win, verch, verch, horch, horch,
                     (const cchar_t *)NULL, (const cchar_t *)NULL,
                     (const cchar_t *)NULL, (const cchar_t *)NULL);
}

int whline_set(WINDOW *win, const cchar_t *wch, int n)
{
  PDC_LOG(("whline_set() - called\n"));

  return wch ? whline(win, *wch, n) : ERR;
}

int hline_set(const cchar_t *wch, int n)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("hline_set() - called\n"));

  return whline_set(stdscr, wch, n);
}

int mvhline_set(int y, int x, const cchar_t *wch, int n)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("mvhline_set() - called\n"));

  if (move(y, x) == ERR)
    {
      return ERR;
    }

  return whline_set(stdscr, wch, n);
}

int mvwhline_set(WINDOW *win, int y, int x, const cchar_t *wch, int n)
{
  PDC_LOG(("mvwhline_set() - called\n"));

  if (wmove(win, y, x) == ERR)
    {
      return ERR;
    }

  return whline_set(win, wch, n);
}

int wvline_set(WINDOW *win, const cchar_t *wch, int n)
{
  PDC_LOG(("wvline_set() - called\n"));

  return wch ? wvline(win, *wch, n) : ERR;
}

int vline_set(const cchar_t *wch, int n)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("vline_set() - called\n"));

  return wvline_set(stdscr, wch, n);
}

int mvvline_set(int y, int x, const cchar_t *wch, int n)
{
#ifdef CONFIG_PDCURSES_MULTITHREAD
  FAR struct pdc_context_s *ctx = PDC_ctx();
#endif
  PDC_LOG(("mvvline_set() - called\n"));

  if (move(y, x) == ERR)
    {
      return ERR;
    }

  return wvline_set(stdscr, wch, n);
}

int mvwvline_set(WINDOW *win, int y, int x, const cchar_t *wch, int n)
{
  PDC_LOG(("mvwvline_set() - called\n"));

  if (wmove(win, y, x) == ERR)
    {
      return ERR;
    }

  return wvline_set(win, wch, n);
}
#endif
